//
//  GameLayer.m
//  GameDemo
//
//  Created by cmpak on 5/10/10.
//  Copyright 2010 thefirstgood.com. All rights reserved.
//

#import "GameLayer.h"

#define IMG_WIDTH 1600

enum {
    kTag_Parallax,
	kTag_Enemy,
    kTag_Prince,
	kTag_Effect,
    kTag_PrinceSpriteSheet,
    kTag_ControlButtonPressed,
    kTag_ControlButton
};


@implementation GameLayer

@synthesize rightSprite, rightPressedSprite, leftSprite, leftPressedSprite;
@synthesize kickSprite, kickPressedSprite;
@synthesize princeSprite, princeWalkAnimate, princeKickAnimate;
@synthesize enemySpriteSheet, enemyWalkAnimation;

- (id) init {
    if( (self=[super init]) ) {
        // CCLayer가 터치이벤트를 처리할 수 있도록 활성화시킵니다.
        self.isTouchEnabled = YES;
        
        // 화면의 픽셀 크기를 구합니다.
        winSize = [[CCDirector sharedDirector] winSize];
        
        [self createBackgroundParallax];
        //[self createPrinceSprite];
        [self createPrinceAndAnimation];
		[self createEnemyAndAnimation];
        [self createArrowButtons];
    }
    
    return self;
}

- (void) onExit {
    [super onExit];
    
    // sprite sheet texture 캐시를 모두 지웁니다.   
    // 더 이상 사용하지않는 캐시를 반드시 지워주세요.
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
}

- (void) createEnemyAndAnimation {
	[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"enemy_walk.plist"];
	
	self.enemySpriteSheet = [CCSpriteSheet spriteSheetWithFile:@"enemy_walk.png"];
	[self addChild:self.enemySpriteSheet z:kTag_Enemy];
	
	NSMutableArray *aniFrames = [NSMutableArray array];
	
	for (NSInteger idx = 1; idx <= 15; idx++) {
		CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache]
								spriteFrameByName:[NSString stringWithFormat:@"zombie_walk_%04d.png", idx]];
		[aniFrames addObject:frame];
	}
	
	CCAnimation *walkAnimation = [[CCAnimation alloc] initWithName:@"enemy_walk" delay:0.15f frames:aniFrames];
	self.enemyWalkAnimation = walkAnimation;
	[walkAnimation release];
	
	for (NSInteger idx = 0; idx < 2; idx++) {
		CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:@"zombie_walk_0001.png"];
		sprite.anchorPoint = ccp(0.5, 0);
		
		if (idx == 0) {
			sprite.position = ccp(winSize.width / 2 - 100, 13);
			sprite.flipX = YES;
		}
		
		else {
			sprite.position = ccp(winSize.width / 2 + 100, 13);
			sprite.flipX = NO;
		}
		
		[self.enemySpriteSheet addChild:sprite];
		
		CCAnimate *walkAnimate = 
		[CCAnimate actionWithAnimation:self.enemyWalkAnimation restoreOriginalFrame:NO];
		[sprite runAction:[CCRepeatForever actionWithAction:walkAnimate]];
	}
}

- (void) createPrinceAndAnimation {
    // 위치정보 파일을 읽어들여 바로 CCSpriteFrame을 만들어 캐시에 저장합니다.
    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"prince_walk_trim.plist"];
    
    // 프레임을 담을 Array를 만듭니다.
    NSMutableArray *aniFrames = [NSMutableArray array];
    
    // 아주 간단히 프레임을 만들어 배열에 저장할 수 있습니다. 스프라이트 시트를 만들 때 사용된
    // 개별 이미지의 파일 이름을 사용하여 개별 프레임을 읽어들입니다.
    for(NSInteger idx = 1; idx <= 15; idx++) {
        CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] 
                                spriteFrameByName:[NSString stringWithFormat:@"prince_walk_%02d.png", idx]];
        [aniFrames addObject:frame];
    }
    
    // 프레임으로 CCAnimation을 만듭니다.  각 프레임당 시간을 0.05초로 정해줍니다.
    //CCAnimation *animation = [CCAnimation animationWithName:@"prince_walk" delay:0.05f frames:aniFrames];
	CCAnimation *animation = [CCAnimation animationWithFrames:aniFrames delay:0.05f];
    
    // CCAnimation에 action을 줄 CCAnimate를 만듭니다.
    CCAnimate *animate = [[CCAnimate alloc] initWithAnimation:animation restoreOriginalFrame:NO];
    self.princeWalkAnimate = animate;
    [animate release];
    
    // 첫번째 프레임을 만들 때 사용했던 이미지 파일 이름을 사용하여 주인공 sprite를 만듭니다.
    CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:@"prince_walk_01.png"];
    self.princeSprite = sprite;
    [self addChild:self.princeSprite z:kTag_Prince tag:kTag_Prince];
    
    // 위치계산이 편하도록 주인공의 anchorPoint를 가운데 맨 아래로 잡습니다.
    self.princeSprite.anchorPoint = ccp(0.5, 0);
    
    // 주인공을 화면 가운데 맨 아래에서 13픽셀 위에 위치시킵니다.
    self.princeSprite.position = ccp(winSize.width / 2, 13);
    
    [sprite release];
	
	
	// 발차기 애니메이션에 쓰일 스프라이트 시트의 위치정보 파일을 읽어들인다.
	[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"prince_kick_notrim.plist"];
	
	[aniFrames removeAllObjects];
	for(NSInteger idx = 1; idx <= 15; idx++) {
        CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] 
                                spriteFrameByName:[NSString stringWithFormat:@"prince_kick_%02d.png", idx]];
        [aniFrames addObject:frame];
    }
    
    // 프레임으로 CCAnimation을 만듭니다.  각 프레임당 시간을 0.04초로 정해줍니다.
    //animation = [CCAnimation animationWithName:@"prince_kick" delay:0.04f frames:aniFrames];
	animation = [CCAnimation animationWithFrames:aniFrames delay:0.04f];
    
    // CCAnimation에 action을 줄 CCAnimate를 만듭니다.
    animate = [[CCAnimate alloc] initWithAnimation:animation restoreOriginalFrame:NO];
    self.princeKickAnimate = animate;
    [animate release];
}

- (void) handleKickHit:(CGPoint)effectPoint enemySprite:(CCSprite*)enemy 
	   directionToFall:(EnemyFallDirection)fallDirection {
	CCSprite *hitEffectSprite = [CCSprite spriteWithFile:@"hit_effect.png"];
	hitEffectSprite.position = effectPoint;
	[self addChild:hitEffectSprite z:kTag_Effect];
	
	hitEffectSprite.scale = 0.3;
	
	[hitEffectSprite runAction:[CCSequence actions:
									[CCDelayTime actionWithDuration:0.05],
									[CCScaleTo actionWithDuration:0.4 scale:1.0],
									[CCCallFuncN actionWithTarget:self
													  selector:@selector(hitEffectCompleteHandler:)], nil]
	 ];
	CGFloat xDiff = 120;
	CGFloat angle = 90;
	if(fallDirection == kFallLeft) {
		xDiff = -120;
		angle = -90;
	}
	
	CGPoint targetPoint = CGPointMake(effectPoint.x + xDiff, enemy.position.y);
	
	id spawn = [CCSpawn actions:
				[CCJumpTo actionWithDuration:0.7 position:targetPoint height:80 jumps:1],
				[CCRotateBy actionWithDuration:0.5 angle:angle], nil];
	[enemy runAction:[CCSequence actions:
					  [CCEaseIn actionWithAction:spawn rate:2.0f],
					  [CCCallFuncND actionWithTarget:self 
											selector:@selector(enemyFallCompleteHandler:enemySprite:)
												data:(void*)enemy],
					  nil]];
}

- (void) hitEffectCompleteHandler:(CCNode*)node {
	if(node != nil)
		[self removeChild:node cleanup:YES];
}

- (void) enemyFallCompleteHandler:(CCNode*)node enemySprite:(CCSprite*)enemy {
	[self.enemySpriteSheet stopAllActions];
	
	if(enemy != nil)
		[self.enemySpriteSheet removeChild:enemy cleanup:YES];
}

- (void) createArrowButtons {
    // 왼쪽 화살표
    CCSprite *sprite = [[CCSprite alloc] initWithFile:@"arrow_left.png"];
    self.leftSprite = sprite;
    
    // 기본 anchorPoint가 가운데, 즉 (0.5, 0.5)이므로 이미지의 가로 세로 크기의 반에 5픽셀의 
    // 여유를 두고 화면 아래에 표시합니다.
    self.leftSprite.position = ccp(10 + self.leftSprite.contentSize.width / 2, 
                                   self.leftSprite.contentSize.height / 2 + 5);
    
    [self addChild:self.leftSprite z:kTag_ControlButton];
    [sprite release];
    
    // 눌렸을 때 쓰일 왼쪽 화살표 
    sprite = [[CCSprite alloc] initWithFile:@"arrow_left_s.png"];
    self.leftPressedSprite = sprite;
    
    // self.leftSprite와 똑같은 위치에 표시합니다.
    self.leftPressedSprite.position = self.leftSprite.position;
    
    // 눌렸을 때의 화살표를 하위 z-order로 넣습니다. 그럼, 위에 같은 크기의 화살표가 
    // 똑같은 위치에 있으니까 가려서 화면상에 보이지 않게됩니다.
    [self addChild:self.leftPressedSprite z:kTag_ControlButtonPressed];
    [sprite release];
    
    // 오른쪽 화살표
    sprite = [[CCSprite alloc] initWithFile:@"arrow_right.png"];
    self.rightSprite = sprite;
    
    // 왼쪽 화살표에서 15픽셀 오르쪽에 위치시킵니다.
    self.rightSprite.position = ccp(self.leftSprite.position.x + self.rightSprite.contentSize.width + 15, 
                                    self.leftSprite.position.y);
    [self addChild:self.rightSprite z:kTag_ControlButton];
    [sprite release];
    
    // 눌렸을 때 쓰일 오른쪽 화살표 
    sprite = [[CCSprite alloc] initWithFile:@"arrow_right_s.png"];
    self.rightPressedSprite = sprite;
    
    // self.rightSprite와 똑같은 위치에 표시합니다.
    self.rightPressedSprite.position = self.rightSprite.position;
    
    // 눌렸을 때의 화살표를 하위 z-order로 넣습니다. 그럼, 위에 같은 크기의 화살표가 
    // 똑같은 위치에 있으니까 가려서 화면상에 보이지 않게됩니다.
    [self addChild:self.rightPressedSprite z:kTag_ControlButtonPressed];
    [sprite release];
	
	// 발차기 버튼
    sprite = [[CCSprite alloc] initWithFile:@"kick.png"];
    self.kickSprite = sprite;
    self.kickSprite.position = ccp(winSize.width - self.kickSprite.contentSize.width / 2 - 5, self.leftSprite.position.y);
    [self addChild:self.kickSprite z:kTag_ControlButton];
    [sprite release];
	
    // 발차기 버튼 눌렸을 때
    sprite = [[CCSprite alloc] initWithFile:@"kick_s.png"];
    self.kickPressedSprite = sprite;
    self.kickPressedSprite.position = self.kickSprite.position;
    [self addChild:self.kickPressedSprite z:kTag_ControlButtonPressed];
    [sprite release];
}

- (void) createBackgroundParallax {
    // 이미지로 백그라운드에 쓰일 CCSprite를 만듭니다.
    CCSprite *bgSprite1 = [CCSprite spriteWithFile:@"background1.png"];
    CCSprite *bgSprite2 = [CCSprite spriteWithFile:@"background2.png"];
    
    // Transform 할 때 사용되는 anchorPoint를 왼쪽 아래 귀퉁이 (0, 0)로 잡습니다. 
    bgSprite1.anchorPoint = ccp(0, 0);
    bgSprite2.anchorPoint = ccp(0, 0);
    
    // 위에서 만든 sprite를 담을 parent로 CCParallaxNode를 만듭니다.
    CCParallaxNode *voidNode = [CCParallaxNode node];
    
    // 배경 sprite를 Parallax에 넣습니다.
    // parallaxRatio는 가로/세로로 움직이는 속도라고 보시면 되겠습니다. 
    // 우리는 가로로만 움직이므로 y 값을 0으로 줍니다.
    
    // background1.png 파일의 세로 크기가 160 픽셀이기 때문에 positionOffset을 이용하여 
    // 화면 위에 위치하도록 좌표를 조정합니다.
    // 뒤쪽에 깔릴 배경인 background1.png는 좀 더 천천히 움직이도록 parallaxRatio의 x 값을 1보다 작은
    // 0.4로 설정합니다.
    [voidNode addChild:bgSprite1 z:0 parallaxRatio:ccp(0.4f, 0) positionOffset:ccp(0, winSize.height / 2)];    
    [voidNode addChild:bgSprite2 z:1 parallaxRatio:ccp(1.0f, 0) positionOffset:CGPointZero];

    [self addChild:voidNode z:kTag_Parallax tag:kTag_Parallax];
}


#pragma mark -
#pragma mark Game Play

- (void) startMovingBackground {
    // 만약 버튼 두개가 다 눌려졌으면 화면을 이동시키지 않습니다.
    if(isLeftPressed == YES && isRightPressed == YES)
        return;
    
    NSLog(@"start moving");
    [self schedule:@selector(moveBackground)];
    
    // 걷기 애니메이션을 시작합니다.
    [self startPrinceWalking];
}

- (void) stopMovingBackground {
    NSLog(@"stop moving");
    [self unschedule:@selector(moveBackground)];
    
    // 걷기 애니메이션을 멈춥니다.
    [self stopPrinceWalking];
}


- (void) moveBackground {
    // GameLayer에 들어있는 parallax node를 받습니다.
    CCNode *voidNode = [self getChildByTag:kTag_Parallax];
    
    // 매 프레임마다 움직일 거리
    CGPoint moveStep = ccp(3, 0);
    
    // 오른쪽 버튼이 눌려졌을 때는 반대로 움직임
    if(isRightPressed)
        moveStep.x = -moveStep.x;
    
    CGFloat bgParallaxRatio = 1.0f;
    
    CGPoint newPos = ccp(voidNode.position.x + moveStep.x, voidNode.position.y);
    
    // 배경이 양쪽 끝에 도달하면 더 이상 움직이지 않음
    if(isLeftPressed == YES && newPos.x > 0)
        newPos.x = 0;
    else if(isRightPressed == YES && newPos.x < -(IMG_WIDTH - winSize.width) / bgParallaxRatio)
        newPos.x = -(IMG_WIDTH - winSize.width) / bgParallaxRatio;
    
    // 주인공이 화면 가운데 있을 경우에만 배경을 움직입니다.
    CGFloat halfWinWidth = winSize.width / 2;
    if(self.princeSprite.position.x == halfWinWidth)
        voidNode.position = newPos;
    
    
    // 주인공의 방향을 정합니다.
    // flipX를 이용하여 하나의 이미지로 두 방향을 다 표현할 수 있습니다.
    if(isRightPressed == YES)
        self.princeSprite.flipX = NO;
    else
        self.princeSprite.flipX = YES;
    
    // 만약 주인공이 화면 가운데 있지않을 경우에는 주인공을 화면가운데까지
    // 이동하게 합니다.
    
    if(isRightPressed == YES && self.princeSprite.position.x < halfWinWidth) {
        // moveStep.x의 부호를 바꾼 이유는 배경과 주인공의 움직임 방향이 서로 반대이기 때문입니다.
        self.princeSprite.position = ccp(self.princeSprite.position.x + (moveStep.x * -1),
                                         self.princeSprite.position.y);
        // 가운데 이상 움직이지 않도록 체크합니다.
        if(self.princeSprite.position.x > halfWinWidth)
            self.princeSprite.position = ccp(halfWinWidth, self.princeSprite.position.y);
    }else if(isLeftPressed == YES && self.princeSprite.position.x > halfWinWidth) {
        // moveStep.x의 부호를 바꾼 이유는 배경과 주인공의 움직임 방향이 서로 반대이기 때문입니다.
        self.princeSprite.position = ccp(self.princeSprite.position.x + (moveStep.x * -1),
                                         self.princeSprite.position.y);
        
        // 가운데 이상 움직이지 않도록 체크합니다.
        if(self.princeSprite.position.x < halfWinWidth)
            self.princeSprite.position = ccp(halfWinWidth, self.princeSprite.position.y);
    }
    
    // 배경의 끝에 도달하면 배경은 움직이지 않고 주인공을 화면 끝까지 이동시킵니다.
    if(newPos.x == 0 || newPos.x == -(IMG_WIDTH - winSize.width)) {
        CGPoint newPrincePos = ccp(self.princeSprite.position.x + (moveStep.x * -1), self.princeSprite.position.y);
        
        // 주인공이 화면의 왼쪽 또는 오른쪽 끝까지 도달했을 때는 더 이상 움직이지 않습니다.
        CGFloat halfWidth = self.princeSprite.contentSize.width / 2;
        if(newPrincePos.x <= halfWidth)
            newPrincePos.x = halfWidth;
        else if(newPrincePos.x >= winSize.width - halfWidth)
            newPrincePos.x = winSize.width - halfWidth;
        
        self.princeSprite.position = newPrincePos;
    }
}

- (void) startPrinceWalking {
    NSLog(@"주인공 걷기 시작");
    [self.princeSprite runAction:[CCRepeatForever actionWithAction:self.princeWalkAnimate]];
}

- (void) stopPrinceWalking {
    NSLog(@"주인공 걷기 끝");
    [self.princeSprite stopAllActions];
}

- (void) kickAnimateCompleteHandler {
    // 버튼을 다시 보이도록 합니다.
    self.kickSprite.visible = YES;
	
    [self.princeSprite stopAllActions];
	isAnimating = NO;
}

#pragma mark -
#pragma mark Touch Event Handling

// 터치가 버튼 Sprite안에서 이루어졌는지 확인합니다.
- (BOOL) isTouchInside:(CCSprite*)sprite withTouch:(UITouch*)touch {
    // Cocoa 좌표 
	CGPoint location = [touch locationInView: [touch view]];
    
    // Cocoa 좌표를 cocos2d 좌표로 변환합니다
	CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
    
    CGFloat halfWidth = sprite.contentSize.height / 2.0;
    CGFloat halfHeight = sprite.contentSize.height / 2.0;
    
    if(convertedLocation.x > (sprite.position.x + halfWidth) ||
       convertedLocation.x < (sprite.position.x - halfWidth) ||
       convertedLocation.y < (sprite.position.y - halfHeight) ||
       convertedLocation.y > (sprite.position.y + halfHeight) ) {
        return NO;
    }
    
    return YES;
}

// 손가락이 닫는 순간 호출됩니다.
- (void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
	// 발차기를 하고 있는 동안에는 움직일 수 없도록 합니다. 
	// 또한 발차기 애니메이션이 진행되는 동안에는 다시 발차기를 할 수 없습니다.
	if(isAnimating == YES) {
        NSLog(@"Touch Began ignored");
        return;
    }
	
    UITouch *touch = [touches anyObject];
    
	// 발차기 버튼이 터치되었는지 검사
    if([self isTouchInside:self.kickSprite withTouch:touch] == YES) {
        self.kickSprite.visible = NO;
		
		NSArray *enemies = [self.enemySpriteSheet children];
		for (CCSprite *enemy in enemies) {
			if (self.princeSprite.flipX == NO &&
				self.princeSprite.position.x + 40 >= enemy.position.x - 30 &&
				 self.princeSprite.position.x + 40 <= enemy.position.x + 30) {
				[self handleKickHit:CGPointMake(self.princeSprite.position.x + 55,
												self.princeSprite.position.y + 113)
						enemySprite:enemy directionToFall:kFallRight];
			}
				
			else if (self.princeSprite.flipX == YES &&
					 self.princeSprite.position.x - 40 <= enemy.position.x + 30 &&
					 self.princeSprite.position.x - 40 >= enemy.position.x - 30)
			{
				[self handleKickHit:CGPointMake(self.princeSprite.position.x - 55, 
												self.princeSprite.position.y + 113)
						enemySprite:enemy directionToFall:kFallLeft];				
			}
		}
		
		isAnimating = YES;  // 발차기 애니메이션이 진행 중
		
        [self.princeSprite runAction:[CCSequence actions:
									  self.princeKickAnimate,
									  [CCCallFunc actionWithTarget:self selector:@selector(kickAnimateCompleteHandler)],
									  nil]];
    }
	
    // 아래 Boolean 변수 대신에 leftSprite와 rightSprite의 visible의 값을 직접 사용해도 무방합니다.
    isLeftPressed = NO;
    isRightPressed = NO;
    
    // 터치가 왼쪽 또는 오른쪽 화살표 안에 들어왔는지 확인합니다.
    if([self isTouchInside:self.leftSprite withTouch:touch] == YES) {
        // 왼쪽 화살표를 안보이게 합니다. 그럼 그 아래에 있던 눌릴 때 보여지는 이미지가 나타날 것입니다.
        self.leftSprite.visible = NO;
        
        isLeftPressed = YES;
    }else if([self isTouchInside:self.rightSprite withTouch:touch] == YES) {
        // 오른쪽 화살표를 안보이게 합니다.
        self.rightSprite.visible = NO;
        
        isRightPressed = YES;
    }
    
    // 버튼이 눌려졌으면 화면을 움직입니다.
    if(isLeftPressed == YES || isRightPressed == YES)
        [self startMovingBackground];
}

// 손가락을 떼는 순간 호출됩니다.
- (void)ccTouchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    // 배경화면을 멈춥니다.
    if(isLeftPressed == YES || isRightPressed == YES)
        [self stopMovingBackground];
    
    // 감춰졌던 버튼이미지를 다시 보이게 합니다.
    if(isLeftPressed == YES)
        self.leftSprite.visible = YES;
    
    if(isRightPressed == YES) 
        self.rightSprite.visible = YES;
}

// 손가락을 움직일 때 계속해서 호출됩니다.
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    
    // 손가락이 버튼을 벗어나면 움직임을 중단합니다.
    if(isLeftPressed == YES && [self isTouchInside:self.leftSprite withTouch:touch] == NO) {
        self.leftSprite.visible = YES;
        [self stopMovingBackground];
    }else if(isRightPressed == YES && [self isTouchInside:self.rightSprite withTouch:touch] == NO) {
        self.rightSprite.visible = YES;
        [self stopMovingBackground];
    }
}


//- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}

#pragma mark -
#pragma mark Memory Release

- (void) dealloc {
    [rightSprite release];
    [rightPressedSprite release];
    [leftSprite release];
    [leftPressedSprite release];
	[kickSprite release];
    [kickPressedSprite release];
    
    [princeSprite release];
    [princeWalkAnimate release];
	[princeKickAnimate release];
    
	[enemySpriteSheet release];
	[enemyWalkAnimation release];
	
    [super dealloc];
}

@end